package com.frontop.terminal.rmonclient.compress;

import java.io.ByteArrayOutputStream;
import java.util.Arrays;

/**
 * @author matrixy 优化：ccc
 * @date 2018/4/11 2021/6/4
 */
@SuppressWarnings("AlibabaUndefineMagicConstant")
public class RLEncoding extends BaseCompressProcessor {
    /**
     * 压缩后的图像字节数组
     */
    private static final ByteArrayOutputStream COMPRESSED_DATA = new ByteArrayOutputStream(1024 * 1024 * 2);

    /**
     * 以RGB作为数组下标的数组容器，用于保存颜色的出现次数，或是颜色表的下标
     */
    private static final int[] COLOR_TABLE = new int[1 << 24];

    /**
     * 保存己出现的颜色，假设同屏最多出现1920 * 1080种不同的颜色
     */
    private static final int[] COLORS = new int[1920 * 1080];

    /**
     * 最少需要N次出现才会加入到颜色表中
     */
    private static final int N = 20;

    /**
     * 最多保存256个出现最多次的颜色
     */
    private static final int[] MAIN_COLORS = new int[510];

    /**
     * mainColors数组的有效数据下标，也指代了颜色个数
     */
    private static int colorIndex = 0;

    /**
     * 查找次数出现最多的颜色
     *
     * @param bitmap 位图
     * @param from   从……起
     * @param to     到
     */
    public static void findMainColors(int[] bitmap, int from, int to) {
        // 重置
        colorIndex = 0;
        Arrays.fill(MAIN_COLORS, 0);

        // 颜色计数
        for (int i = from; i < to; i++) {
            int color = detract(bitmap[i] & 0xffffff);
            if (bitmap[i] == 0) {
                continue;
            }
            if (COLOR_TABLE[color] == 0) {
                COLORS[colorIndex++] = color;
            }
            COLOR_TABLE[color] += 1;
        }

        // 查找主颜色
        int minCount = 0;
        for (int i = 0; i < colorIndex; i++) {
            int color = COLORS[i];
            int count = COLOR_TABLE[color];

            // 将colorCounting清零
            COLOR_TABLE[color] = 0;

            if (count < N) {
                continue;
            }

            // 如果比mainColors里最小的都还要少，后面的事情也不用弄了
            if (count < minCount) {
                continue;
            }

            int k = 0;
            for (; k < MAIN_COLORS.length; k += 2) {
                if (MAIN_COLORS[k] < count) {
                    if (k < MAIN_COLORS.length - 2) {
                        System.arraycopy(MAIN_COLORS, k, MAIN_COLORS, k + 2, MAIN_COLORS.length - k - 2);
                    }

                    MAIN_COLORS[k] = count;
                    MAIN_COLORS[k + 1] = color;
                    break;
                }
            }
            minCount = MAIN_COLORS[MAIN_COLORS.length - 2];
        }

        colorIndex = 1;
        for (int i = 0; i < MAIN_COLORS.length; i += 2) {
            int count = MAIN_COLORS[i];
            if (count == 0) {
                continue;
            }
            COLOR_TABLE[MAIN_COLORS[i + 1]] = colorIndex++;
        }
    }

    /**
     * 针对颜色值进行减位，用于压缩处理
     *
     * @param c 灰色RGB不减位
     * @return int
     */
    static int detract(int c) {
        // 灰色RGB不减位
        if ((((c >> 16) & 0xff) ^ ((c >> 8) & 0xff)) == (c & 0xff)) {
            return c;
        }
        return c & 0xf0f0f0;
    }

    public static void init() {
        // 初始化
    }

    @Override
    public synchronized byte[] compress(int[] bitmap, int from, int to) {
        // 初始化
        COMPRESSED_DATA.reset();

        // 查找出现次数最多的颜色，建立颜色表
        findMainColors(bitmap, from, to);

        // 写入颜色表
        COMPRESSED_DATA.write((byte) ((colorIndex - 1) & 0xff));
        for (int i = 0; i < colorIndex - 1; i++) {
            int rgb = MAIN_COLORS[i * 2 + 1] & 0xffffff;
            COMPRESSED_DATA.write((rgb >> 16) & 0xff);
            COMPRESSED_DATA.write((rgb >> 8) & 0xff);
            COMPRESSED_DATA.write(rgb & 0xff);
        }

        // 行程编码
        int rl = 1;
        int color, lastColor = bitmap[0] & 0xffffff;
        for (int i = 1; i < to; i++) {
            color = bitmap[i];
            if (color == lastColor && rl < 32767) {
                rl += 1;
                continue;
            }
            if (lastColor == 0) {
                COMPRESSED_DATA.write((rl | 0x8000) >> 8);
                COMPRESSED_DATA.write(rl);
                COMPRESSED_DATA.write(0);
            } else if (COLOR_TABLE[detract(lastColor & 0xffffff)] > 0) {
                COMPRESSED_DATA.write((rl | 0x8000) >> 8);
                COMPRESSED_DATA.write(rl);
                COMPRESSED_DATA.write(COLOR_TABLE[detract(lastColor & 0xffffff)]);
            } else {
                lastColor = detract(lastColor & 0xffffff);
                COMPRESSED_DATA.write((rl & 0x7fff) >> 8);
                COMPRESSED_DATA.write(rl);
                COMPRESSED_DATA.write((byte) ((lastColor >> 16) & 0xff));
                COMPRESSED_DATA.write((byte) ((lastColor >> 8) & 0xff));
                COMPRESSED_DATA.write((byte) (lastColor & 0xff));
            }
            rl = 1;
            lastColor = color;
        }
        if (lastColor == 0) {
            COMPRESSED_DATA.write((rl | 0x8000) >> 8);
            COMPRESSED_DATA.write(rl);
            COMPRESSED_DATA.write(0);
        } else if (COLOR_TABLE[detract(lastColor & 0xffffff)] > 0) {
            COMPRESSED_DATA.write((rl | 0x8000) >> 8);
            COMPRESSED_DATA.write(rl);
            COMPRESSED_DATA.write(COLOR_TABLE[detract(lastColor & 0xffffff)]);
        } else {
            lastColor = detract(lastColor & 0xffffff);
            COMPRESSED_DATA.write((rl & 0x7fff) >> 8);
            COMPRESSED_DATA.write(rl);
            COMPRESSED_DATA.write((byte) ((lastColor >> 16) & 0xff));
            COMPRESSED_DATA.write((byte) ((lastColor >> 8) & 0xff));
            COMPRESSED_DATA.write((byte) (lastColor & 0xff));
        }

        // 清空colortable
        for (int i = 0; i < MAIN_COLORS.length; i += 2) {
            COLOR_TABLE[MAIN_COLORS[i + 1]] = 0;
        }

        return COMPRESSED_DATA.toByteArray();
    }

    /**
     * 压缩后的图像数据解压
     *
     * @param width          宽度
     * @param height         高度
     * @param compressedData 压缩数据
     * @return int[]
     */
    @SuppressWarnings("unused")
    public int[] decompress(int width, int height, byte[] compressedData) {
        int[] bitmap = new int[width * height];
        for (int k = 0, i = ((compressedData[0] & 0xff) * 3) + 1; i < compressedData.length; ) {
            int rl = (((compressedData[i] & 0xff) << 8) | (compressedData[i + 1] & 0xff)) & 0xffff;
            int red, green, blue;
            if ((rl & 0x8000) > 0) {
                int index = (compressedData[i + 2] & 0xff);
                if (index == 0) {
                    k += (rl & 0x7fff);
                    i += 3;
                    continue;
                }
                index = (index - 1) * 3 + 1;
                red = compressedData[index] & 0xff;
                green = compressedData[index + 1] & 0xff;
                blue = compressedData[index + 2] & 0xff;
                i += 3;
            } else {
                red = compressedData[i + 2] & 0xff;
                green = compressedData[i + 3] & 0xff;
                blue = compressedData[i + 4] & 0xff;
                i += 5;
            }
            for (int s = 0, l = rl & 0x7fff; s < l; s++) {
                bitmap[k++] = 0xff000000 | (red << 16) | (green << 8) | blue;
            }
        }
        return bitmap;
    }
}
